﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;

namespace ANTLR01
{
    public static class ExpressionEx
    {
        /// <summary>
        /// Returns list of Convert expressions converting arguments to parameter types.
        /// </summary>
        public static Expression[] ConvertArguments(DynamicMetaObject[] args, ParameterInfo[] parms)
        {
            Expression[] callArgs = new Expression[args.Length];
            for (int index = 0; index < args.Length; index++)
            {
                callArgs[index] = Expression.Convert(args[index].Expression, parms[index].ParameterType);
            }
            return callArgs;
        }

        public static Expression DynamicInvoke(Expression expr, params Expression[] args)
        {
            List<Expression> finalArgs = new List<Expression>(args);
            finalArgs.Insert(0, expr);
            return Expression.Dynamic(
                new SimpleInvokeBinder(new CallInfo(args.Length)),
                typeof(object),
                finalArgs);
        }

        /// <summary>
        /// Process two dynamic values.
        /// </summary>
        public static Expression DynamicBinary(ExpressionType type, Expression left, Expression right)
        {
            return Expression.Dynamic(
                        new SimpleBinaryOperationBinder(type),
                        typeof(object),
                        left,
                        right);
        }

        /// <summary>
        /// Negate a dynamic value.
        /// </summary>
        public static Expression DynamicUnary(ExpressionType type, Expression expr)
        {
            return Expression.Dynamic(
                        new SimpleUnaryOperationBinder(ExpressionType.Negate),
                        typeof(object),
                        expr);
        }

        /// <summary>
        /// Retrieve a variable from the input scope by name.
        /// </summary>
        /// <param name="scope">An expression which refers to an object of type Scope.</param>
        /// <param name="symbolName">A symbol name within the given SimpleObject object.</param>
        public static Expression SymbolExpression(Expression scope, string symbolName)
        {
            if (scope.Type != typeof(SimpleObject))
            {
                throw new ArgumentException("The scope expression must represent a SimpleObject.");
            }
            return Expression.Property(Expression.Call(scope, "GetSymbol", null, Expression.Constant(symbolName)), "Data");
        }

        /// <summary>
        /// Ensure that some sort of object type is returned by the expression.
        /// </summary>
        public static Expression EnsureObjectResult(Expression expr)
        {
            if (!expr.Type.IsValueType)
            {
                return expr;
            }
            if (expr.Type == typeof(void))
            {
                return Expression.Block(expr, Expression.Default(typeof(object)));
            }
            else
            {
                return Expression.Convert(expr, typeof(object));
            }
        }

        /// <summary>
        /// Convert a boolean expression into a double, then into an object.
        /// </summary>
        public static Expression ToDynamicDouble(Expression expr)
        {
            return Expression.Convert(Expression.Call(typeof(Convert).GetMethod("ToDouble", new[] { typeof(object) }), expr), typeof(object));
        }

        /// <summary>
        /// Convert a double expression into a boolean, then into an object.
        /// </summary>
        public static Expression ToDynamicBoolean(Expression expr)
        {
            return Expression.Convert(Expression.Call(typeof(Convert).GetMethod("ToBoolean", new[] { typeof(object) }), expr), typeof(object));
        }

        public static Expression ToBoolean(Expression expr)
        {
            return Expression.Call(typeof(Convert).GetMethod("ToBoolean", new[] { typeof(object) }), expr);
        }
    }
}